<template>
	<view>
		<u-action-sheet round="10" :title="title" @close="close" :safeAreaInsetBottom="true" :show="showAgent">
			<scroll-view :scroll-y="true" class="border-top pb-10" :style="{height: height}">
				<u-checkbox-group>
					<view class="flex-col">
						<view class="w-375 px-15" v-for="(node, index) in treeList" :key="node.key">
							<view class="tree-node flex-row items-center"
								:style="{ marginLeft: `${node.rank * 30}rpx`,display:'flex'}"
								v-if="isShow(node.key) || node.rank === 0">
								<u-checkbox :checked="node.checked" @change="checkboxChange(node)" class="checkbox-item"
									:activeColor="checkColor" :name="node.key"></u-checkbox>

								<block v-if="node.childrenid && node.childrenid.length > 0">
									<u-icon :name="isOpen(node.key) ? 'arrow-down' : 'arrow-right'" size="18"
										@click="toggleTree(!isOpen(node.key), node,(node.childrenid && node.childrenid.length > 0))"></u-icon>
								</block>
								<view @click="checkboxChange(node)" class="ml-5" v-if="node.avatar">
									<u-image shape="circle" :src="node.avatar" width="60rpx" height="60rpx"></u-image>
								</view>
								<text class="text-14 pl-6"
									@click="toggleTree(!isOpen(node.key), node,(node.childrenid && node.childrenid.length > 0))">{{ node.name }}</text>
							</view>
						</view>
					</view>
				</u-checkbox-group>
			</scroll-view>
		</u-action-sheet>
	</view>
</template>

<script>
	export default {
		name: 'uni-byt-checkbox-tree',
		// close:弹窗关闭  checkChange:选择元素回调
		emits: ['close', 'checkChange'],
		props: {
			// 弹窗打开
			show: {
				type: Boolean,
				default: false
			},
			// 弹窗标题
			title: {
				type: String,
				default: '请选择'
			},
			// 弹窗高度
			height: {
				type: String,
				default: '50vh'
			},
			// 多选按钮颜色
			checkColor: {
				type: String,
				default: '#EA0106'
			},
			// 树结构数据源
			treeData: {
				type: Array,
				default: function() {
					return [];
				}
			},
			// 替换节点fields属性值，默认为 { children:'children', key:'key', name:'name',avatar: 'avatar' }
			replaceFields: {
				type: Object,
				default: function() {
					return {
						children: 'children',
						key: 'key',
						name: 'name',
						avatar: 'avatar'
					};
				}
			},
			// 默认选中节点数组，传入此数组可指定哪些节点默认选中
			// 如果传入的key包含父节点 则默认选中所有其子节点
			defaultCheckedKeys: {
				type: Array,
				default: function() {
					return [];
				}
			},
			// 默认打开节点数组，传入此数组可指定哪些节点默认打开
			defaultOpenedKeys: {
				type: Array,
				default: function() {
					return [];
				}
			},
			// 默认选中的名称id数组
			defaultCheckedList: {
				type: Array,
				default: function() {
					return [];
				}
			},
		},
		watch: {
			treeData: {
				handler: function(nVal, oVal) {
					this.loadTree()
				},
				deep: true,
				immediate: true
			}
		},
		data() {
			return {
				openedList: [], // 展开节点数组（只存放带有子级的节点）
				checkedKeyList: [], // 当前选中节点数组
				checkedList: [], // 当前选中节点名称数组
				treeList: [], // 扁平化后的树数据源
				showList: [] // 当前展示的树节点（0级节点默认添加到数组中始终显示，其他级别动态控制显隐）
			};
		},
		computed: {
			// 双向绑定弹窗打开
			showAgent: {
				get() {
					return this.show
				},
				set(val) {
					this.$emit("update:show", val)
				}
			},
			// 判断节点展开/关闭
			isOpen() {
				return function(key) {
					return this.openedList.includes(key);
				};
			},
			// 判断节点显示/隐藏
			isShow() {
				return function(key) {
					return this.showList.includes(key);
				};
			}
		},
		methods: {
			// 弹窗关闭
			close() {
				this.showAgent = false
				this.$emit('close')
			},
			// 初始化树方法，可在初始化及reload刷新时调用重新渲染树结构
			loadTree() {
				this.resetData()
				this.renderTreeList();
			},
			resetData() {
				this.openedList = []
				this.checkedKeyList = []
				this.treeList = []
				this.showList = []
			},
			// 渲染树方法 将树数据扁平化并处理初始选中、打开状态
			// FIXME:渲染完成后传出事件 afterTreeInit 参数为 this.treeList (扁平化后的数组list)、this.checkedKeyList (当前默认选中的数组)
			renderTreeList() {
				const replaceFields = this.replaceFields;
				this.treeList = [...getListByTree(this.treeData)]

				// 树结构数据扁平化处理
				function getListByTree(treeData) {
					const treeList = [];
					const treeToList = function(list = [], rank = 0, parentId = [], parents = []) {
						list.forEach((item, index) => {
							const node = {
								key: item[replaceFields.key],
								name: item[replaceFields.name],
								avatar: item[replaceFields.avatar],
								source: item,
								parentId, // 父级id数组
								parents, // 父级id数组
								rank, // 层级
								checked: false
							};
							if (Array.isArray(item[replaceFields.children]) && item[replaceFields.children]
								.length > 0) {
								// 在这里声明父节点属性是因为第一个节点一定不会有父节点,所以从第二条开始才生效
								let parentid = [...parentId],
									parentArr = [...parents],
									childrenid = [...childrenid];
								delete parentArr[replaceFields.children];
								parentid.push(item[replaceFields.key]);
								parentArr.push({
									[replaceFields.key]: item[replaceFields.key],
									[replaceFields.name]: item[replaceFields.name],
									[replaceFields.avatar]: item[replaceFields.avatar]
								});
								// 这里的子节点使用直接赋值的方式是因为第一个节点就有可能存在子节点
								node.childrenid = item[replaceFields.children].map(childItem => {
									return childItem[replaceFields.key];
								});
								node.childrenName = item[replaceFields.children].map(childItem => {
									return {
										name: childItem[replaceFields.name],
										key: childItem[replaceFields.key]
									};
								});
								treeList.push(node);
								treeToList(item[replaceFields.children], rank + 1, parentid, parentArr);
							} else {
								treeList.push(node);
							}
						});
					}
					treeToList(treeData)
					return treeList
				}
				// 遍历扁平化后的数组 根据初始参数如defaultCheckedList和defaultOpenedKeys渲染树节点初始状态
				this.checkedKeyList = [...this.defaultCheckedKeys]
				this.checkedList = [...this.defaultCheckedList]

				this.treeList.forEach(node => {
					const nodeItem = this.treeList.find(n => n.key === node.key);
					// 如果有节点的key存在checkedKeyList中且有childrenid则直接合并到checkedKeyList中
					if (this.checkedKeyList.includes(node.key)) {
						node.checked = true;
					}
					// 根据默认打开数组控制树节点默认打开
					if (this.defaultOpenedKeys.includes(node.key)) {
						if (nodeItem && nodeItem.parentId && nodeItem.parentId.length > 0) {
							this.openedList = [...new Set([...this.openedList, ...nodeItem.parentId])]
							// 如果存在父节点 默认打开父节点并将父节点放进openedList中
							nodeItem.parentId.forEach(pKey => {
								const parentNode = this.treeList.find(n => n.key === pKey);
								this.toggleTree(true, parentNode)
							})
						}
					}
				});
				// FIXME: 2023年3月30日15:43:03 修复同节点下子孙节点都默认添加时上级节点重复添加导致图标显示异常问题
				this.openedList = [...new Set(this.openedList)]
				// 多层结构时合并后的数组会重复,这里进行去重处理
				this.checkedKeyList = [...new Set([...this.defaultCheckedKeys, ...this.checkedKeyList])];
				this.checkedControl(this.checkedKeyList, true);
				this.$emit('afterTreeInit', this.treeList, this.checkedKeyList)
			},
			// 节点选中事件
			// FIXME: 选中后传出事件 checkChange参数为node(当前选中节点) this.checkedKeyList(当前选中节点的keys)
			// 查出选择id的所有选项
			checkboxChange(node) {
				try {
					console.log("节点选中事件1", node);
					node.checked = !node.checked
					const checkedKeyList = this.checkedKeyList;
					const checkedList = this.checkedList;
					const checked = node.checked;
					const key = node.key;
					const name = node.name;
					const hasChildren = node && node.childrenid && node.childrenid.length > 0;
					const hasChildren2 = node && node.childrenName && node.childrenName.length > 0;
					if (!checked) {
						// 取消勾选 若存在子级节点则级联取消所有层级勾选
						let uncheckedList = [];
						this.checkedKeyList = [...this.delItemInArr(checkedKeyList, key)];
						if (hasChildren) {
							uncheckedList = this.generationKeys(node.childrenid, this.treeList);
							uncheckedList.forEach(x => {
								this.checkedKeyList = this.checkedKeyList.filter(k => k !== x);
							});
						}
						// 节点名称
						let uncheckedList2 = [];
						this.checkedList = [...this.delItemInArr2(checkedList, key)];
						if (hasChildren2) {
							uncheckedList2 = this.generationNames(node.childrenName, this.treeList);
							uncheckedList2.forEach(x => {
								this.checkedList = this.checkedList.filter(k => k.key !== x.key);
							});
						}
						this.checkedControl(uncheckedList, checked);
					} else {
						// 勾选 若存在子级节点则级联勾选所有层级
						this.checkedKeyList.push(key);
						if (hasChildren) {
							this.checkedKeyList = [...new Set([...this.checkedKeyList, ...this.generationKeys(node
								.childrenid,
								this
								.treeList)])];
						}
						console.log("勾选checkedKeyList:", this.checkedKeyList.length);
						// 名称
						this.checkedList.push({
							name,
							key
						});
						if (hasChildren2) {
							const arr = [...new Set([...this.checkedList, ...this.generationNames(node
								.childrenName, this.treeList)])]
							let arr2 = []
							arr.forEach(item => {
								const bob = arr2.some(items => items.key == item.key)
								if (!bob) {
									arr2.push(item)
								}
							})
							this.checkedList = arr2;
						}
						console.log("勾选checkedList:", this.checkedList.length);
						this.checkedControl(this.checkedKeyList, checked);
					}
					this.$emit('checkChange', node, checked, this.checkedKeyList, this.checkedList);
				} catch (e) {
					console.log("节点选中事件错误", e);
				}
			},
			// 控制树节点选中状态修改数据并渲染到树中
			checkedControl(checkedKeyList, checked) {
				this.treeList = this.treeList.map(node => {
					if (checkedKeyList.includes(node.key)) {
						node.checked = checked;
					}
					return node;
				});
			},
			// 深度遍历，如果有子级则逐层取出子级的key
			generationKeys(childrenid, treeList) {
				let checkedKeys = [];
				const deepEach = (ids, list) => {
					ids.forEach((id, index) => {
						// console.log('dddidd', index);
						const childNode = list.find(node => node.key === id);
						if (childNode && childNode.childrenid && childNode.childrenid.length > 0) {
							deepEach(childNode.childrenid, list);
							checkedKeys = [...checkedKeys, ...childNode.childrenid, id];
						} else {
							checkedKeys = [...checkedKeys, id];
						}
					});
				};
				deepEach(childrenid, treeList);
				console.log('childrenid:', checkedKeys.length);
				return checkedKeys;
			},
			// 深度遍历，如果有子级则逐层取出子级的name
			generationNames(childrenName, treeList, ) {
				let checkedNames = [];
				const deepEach = (naems, list) => {
					naems.forEach((item, index) => {
						const childNode = list.find(node => node.key === item.key);
						// console.log("scascac", index, childNode, name);
						if (childNode && childNode.childrenName && childNode.childrenName.length > 0) {
							deepEach(childNode.childrenName, list);
							checkedNames = [...checkedNames, ...childNode.childrenName, item];
						} else {
							checkedNames = [...checkedNames, item];
						}
					});
				};
				deepEach(childrenName, treeList);
				console.log('childrenName:', checkedNames.length);
				return checkedNames;
			},
			// 节点关闭打开事件
			toggleTree(opened, node, bob) {
				// 没有子节点点击名称选择
				if (!bob) {
					this.checkboxChange(node)
				}
				const openedList = this.openedList;
				if (!opened) {
					this.openedList = [...this.delItemInArr(openedList, node.key)];
					const closed = [...new Set(this.generationKeys(node.childrenid, this.treeList))];
					// 关闭时先遍历有没有存在子级的节点，如果有则级联关闭
					closed.forEach(x => {
						const childNode = this.treeList.find(node => node.key === x);
						if (childNode && childNode.childrenid) {
							this.openedList = [...this.delItemInArr(this.openedList, childNode.key)];
						}
						// 将关闭节点的子级全部隐藏
						this.showList = this.showList.filter(k => k !== x);
					});
				} else {
					uni.showLoading()

					// 打开时显示所有子级节点
					this.openedList.push(node.key);
					this.showList = [...new Set([...this.openedList, ...this.showList, ...node.childrenid])];

					const num = node.childrenid.length
					setTimeout(() => {
						console.log("展开节点延迟时间", num * 5);
						uni.hideLoading()
						// this.loadingPage = false
					}, num * 6)

				}
			},
			// 删除数组中指定子项方法
			delItemInArr(arr, item) {
				const newArr = arr;
				const index = newArr.findIndex(p => p === item);
				if (index >= 0) {
					newArr.splice(index, 1);
				}
				return newArr;
			},
			// 删除数组中指定子项方法
			delItemInArr2(arr, item) {
				const newArr = arr;
				const index = newArr.findIndex(p => p.key === item);
				if (index >= 0) {
					newArr.splice(index, 1);
				}
				return newArr;
			}
		}
	};
</script>

<style lang="scss" scoped>
	view {
		box-sizing: border-box;
	}

	.checkbox-item {
		padding-left: 8rpx;

		v-deep .u-checkbox__label {
			margin-right: 0;
		}
	}

	.tree-node {
		text-align: left;
		margin-bottom: 8rpx;
		height: 80rpx;
		line-height: 80rpx;
		border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
	}
</style>